package com.rongx.reggie.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rongx.reggie.common.R;
import com.rongx.reggie.dto.DishDto;
import com.rongx.reggie.entity.Category;
import com.rongx.reggie.entity.Dish;
import com.rongx.reggie.entity.DishFlavor;
import com.rongx.reggie.mapper.DishMapper;
import com.rongx.reggie.service.CategoryService;
import com.rongx.reggie.service.DishFlavorService;
import com.rongx.reggie.service.DishService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜品服务实现类
 * 提供菜品相关的业务逻辑处理，包括增删改查、口味管理、批量操作等功能
 */
@Service
@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("all")
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {

    private final DishFlavorService dishFlavorService;
    private final CategoryService categoryService;

    /**
     * 保存菜品信息及其口味数据（事务保证原子性）
     *
     * @param dishDto 包含菜品基本信息和口味列表的数据传输对象
     * @throws IllegalArgumentException 入参非法时抛出
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveWithFlavor(DishDto dishDto) {
        // 1. 入参校验
        if (dishDto == null) {
            throw new IllegalArgumentException("菜品信息不能为空");
        }
        if (dishDto.getFlavors() == null || dishDto.getFlavors().isEmpty()) {
            log.warn("菜品[{}]未关联口味数据", dishDto.getName());
        }

        // 2. 保存菜品基本信息到dish表
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDto, dish);
        this.save(dish);

        // 3. 关联菜品ID到口味数据，批量保存到dish_flavor表
        Long dishId = dish.getId();
        List<DishFlavor> flavors = dishDto.getFlavors();
        if (flavors != null && !flavors.isEmpty()) {
            flavors = flavors.stream()
                    .peek(item -> item.setDishId(dishId)) // 绑定菜品ID
                    .collect(Collectors.toList());
            dishFlavorService.saveBatch(flavors);
        }

        log.info("菜品[{}]及口味数据保存成功，菜品ID：{}", dish.getName(), dishId);
    }

    /**
     * 根据菜品ID查询完整信息（含基本信息+关联口味）
     *
     * @param id 菜品ID（不能为空）
     * @return 包含完整信息的DishDto对象
     * @throws IllegalArgumentException 入参为空时抛出
     * @throws RuntimeException          菜品不存在时抛出
     */
    @Override
    public DishDto getByIdWithFlavor(Long id) {
        // 1. 入参校验
        if (id == null) {
            throw new IllegalArgumentException("菜品ID不能为空");
        }

        // 2. 查询菜品基本信息
        Dish dish = this.getById(id);
        if (dish == null) {
            throw new RuntimeException("菜品不存在，ID：" + id);
        }

        // 3. 转换为DishDto并拷贝基本属性
        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish, dishDto);

        // 4. 查询关联的口味信息
        LambdaQueryWrapper<DishFlavor> flavorWrapper = new LambdaQueryWrapper<>();
        flavorWrapper.eq(DishFlavor::getDishId, dish.getId());
        List<DishFlavor> flavors = dishFlavorService.list(flavorWrapper);
        dishDto.setFlavors(flavors);

        log.info("查询菜品[{}]完整信息成功，关联口味数：{}", dish.getName(), flavors.size());
        return dishDto;
    }

    /**
     * 更新菜品信息及其口味数据（先删后增，保证数据一致性）
     *
     * @param dishDto 包含更新后信息的DishDto对象（必须含菜品ID）
     * @throws IllegalArgumentException 入参非法时抛出
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateWithFlavor(DishDto dishDto) {
        // 1. 入参校验
        if (dishDto == null || dishDto.getId() == null) {
            throw new IllegalArgumentException("菜品ID不能为空");
        }
        if (dishDto.getFlavors() == null || dishDto.getFlavors().isEmpty()) {
            log.warn("菜品[{}]未关联口味数据", dishDto.getName());
        }

        // 2. 更新菜品基本信息（dish表）
        this.updateById(dishDto);
        Long dishId = dishDto.getId();

        // 3. 清理原有口味数据（dish_flavor表）
        LambdaQueryWrapper<DishFlavor> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(DishFlavor::getDishId, dishId);
        dishFlavorService.remove(deleteWrapper);

        // 4. 保存新的口味数据（dish_flavor表）
        List<DishFlavor> newFlavors = dishDto.getFlavors();
        if (newFlavors != null && !newFlavors.isEmpty()) {
            newFlavors = newFlavors.stream()
                    .peek(item -> item.setDishId(dishId)) // 绑定菜品ID
                    .collect(Collectors.toList());
            dishFlavorService.saveBatch(newFlavors);
        }

        log.info("菜品[{}]及口味数据更新成功，菜品ID：{}", dishDto.getName(), dishId);
    }

    /**
     * 分页查询菜品信息（含分类名称，优化N+1查询）
     *
     * @param page     页码（最小为1）
     * @param pageSize 每页条数（1~100）
     * @param name     菜品名称模糊查询（可为空）
     * @return 分页后的DishDto列表（含分类名称）
     * @throws IllegalArgumentException 分页参数非法时抛出
     */
    @Override
    public Page<DishDto> getPage(int page, int pageSize, String name) {
        // 1. 分页参数校验
        if (page < 1) {
            throw new IllegalArgumentException("页码不能小于1");
        }
        if (pageSize < 1 || pageSize > 100) {
            throw new IllegalArgumentException("每页条数必须在1~100之间");
        }

        // 2. 构建分页查询条件
        Page<Dish> dishPage = new Page<>(page, pageSize);
        LambdaQueryWrapper<Dish> dishWrapper = new LambdaQueryWrapper<>();
        // 模糊查询（去空格，避免空字符串查询）
        if (name != null && !name.trim().isEmpty()) {
            dishWrapper.like(Dish::getName, name.trim());
        }
        dishWrapper.orderByDesc(Dish::getUpdateTime); // 按更新时间降序

        // 3. 执行分页查询（dish表）
        this.page(dishPage, dishWrapper);
        List<Dish> dishList = dishPage.getRecords();
        if (dishList.isEmpty()) {
            log.info("分页查询菜品，页码：{}，每页条数：{}，无匹配数据", page, pageSize);
            return new Page<>(page, pageSize); // 返回空分页
        }

        // 4. 批量查询分类信息（优化N+1查询：1次查询替代N次）
        List<Long> categoryIds = dishList.stream()
                .map(Dish::getCategoryId)
                .distinct() // 去重，减少查询量
                .collect(Collectors.toList());
        // 分类ID→分类名称的映射（内存操作，无额外查询）
        Map<Long, String> categoryNameMap = categoryService.listByIds(categoryIds).stream()
                .collect(Collectors.toMap(
                        Category::getId,
                        Category::getName,
                        (k1, k2) -> k1 // 防止分类ID重复（理论上不会）
                ));

        // 5. 转换为DishDto（补充分类名称）
        List<DishDto> dishDtoList = dishList.stream()
                .map(dish -> {
                    DishDto dto = new DishDto();
                    BeanUtils.copyProperties(dish, dto);
                    // 从映射中获取分类名称，避免重复查询数据库
                    dto.setCategoryName(categoryNameMap.getOrDefault(dish.getCategoryId(), ""));
                    return dto;
                })
                .collect(Collectors.toList());

        // 6. 构建并返回分页结果
        Page<DishDto> resultPage = new Page<>();
        BeanUtils.copyProperties(dishPage, resultPage, "records"); // 拷贝分页元数据（总条数、总页数等）
        resultPage.setRecords(dishDtoList);

        log.info("分页查询菜品成功，页码：{}，每页条数：{}，查询结果数：{}", page, pageSize, dishDtoList.size());
        return resultPage;
    }

    /**
     * 更新单个菜品状态（在售/停售）
     *
     * @param status 状态值（0=停售，1=在售）
     * @param id     菜品ID（不能为空）
     * @return 更新后的完整菜品对象，更新失败返回null
     * @throws IllegalArgumentException 入参非法时抛出
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Dish updateStatus(Integer status, Long id) {
        // 1. 入参校验
        if (id == null) {
            throw new IllegalArgumentException("菜品ID不能为空");
        }
        if (status == null || !Arrays.asList(0, 1).contains(status)) {
            throw new IllegalArgumentException("状态值非法，仅支持0（停售）或1（在售）");
        }

        // 2. 校验菜品是否存在
        if (this.getById(id) == null) {
            throw new RuntimeException("菜品不存在，ID：" + id);
        }

        // 3. 执行状态更新
        Dish dish = new Dish();
        dish.setId(id);
        dish.setStatus(status);
        boolean updateSuccess = this.updateById(dish);

        // 4. 返回更新后的完整对象（从数据库重新查询，保证字段完整）
        if (updateSuccess) {
            Dish updatedDish = this.getById(id);
            log.info("菜品[{}]状态更新成功，ID：{}，新状态：{}", updatedDish.getName(), id, status == 1 ? "在售" : "停售");
            return updatedDish;
        } else {
            log.error("菜品状态更新失败，ID：{}", id);
            return null;
        }
    }

    /**
     * 批量更新菜品状态（在售/停售）
     *
     * @param status 状态值（0=停售，1=在售）
     * @param ids    菜品ID字符串（多个ID用逗号分隔，不能为空）
     * @throws IllegalArgumentException 入参非法时抛出
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdateStatus(Integer status, String ids) {
        // 1. 入参校验
        if (status == null || !Arrays.asList(0, 1).contains(status)) {
            throw new IllegalArgumentException("状态值非法，仅支持0（停售）或1（在售）");
        }
        if (ids == null || ids.trim().isEmpty()) {
            throw new IllegalArgumentException("菜品ID列表不能为空");
        }

        // 2. 解析ID列表（去空格、过滤空字符串）
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(String::trim)
                .filter(idStr -> !idStr.isEmpty())
                .map(Long::valueOf)
                .collect(Collectors.toList());
        if (idList.isEmpty()) {
            throw new IllegalArgumentException("菜品ID列表不能为空");
        }

        // 3. 校验所有菜品是否存在
        long existCount = this.count(new LambdaQueryWrapper<Dish>().in(Dish::getId, idList));
        if (existCount != idList.size()) {
            log.warn("批量更新状态的菜品中存在不存在的ID，请求ID数：{}，实际存在数：{}", idList.size(), existCount);
        }

        // 4. 批量构建更新对象
        List<Dish> dishList = idList.stream()
                .map(id -> {
                    Dish dish = new Dish();
                    dish.setId(id);
                    dish.setStatus(status);
                    return dish;
                })
                .collect(Collectors.toList());

        // 5. 批量更新（无需自注入，this调用即可保证事务生效）
        this.updateBatchById(dishList);

        log.info("批量更新菜品状态成功，状态：{}，更新数量：{}，涉及菜品ID：{}",
                status == 1 ? "在售" : "停售", idList.size(), idList);
    }

    /**
     * 根据条件查询菜品列表（含分类名称+关联口味，优化N+1查询）
     *
     * @param dish 查询条件（支持分类ID、状态过滤）
     * @return 完整的菜品DTO列表（含基本信息、分类名称、口味列表）
     */
    @Override
    public List<DishDto> getList(Dish dish) {
        // 1. 构建查询条件
        LambdaQueryWrapper<Dish> dishWrapper = new LambdaQueryWrapper<>();
        // 分类ID过滤（不为空时添加）
        if (dish != null && dish.getCategoryId() != null) {
            dishWrapper.eq(Dish::getCategoryId, dish.getCategoryId());
        }
        // 状态过滤（默认查询在售菜品，支持传入null取消过滤）
        if (dish == null || dish.getStatus() != null) {
            dishWrapper.eq(Dish::getStatus, 1); // 1=在售
        }
        // 排序：先按更新时间降序，再按排序字段升序
        dishWrapper.orderByDesc(Dish::getUpdateTime).orderByAsc(Dish::getSort);

        // 2. 查询菜品基本信息（dish表）
        List<Dish> dishList = this.list(dishWrapper);
        if (dishList.isEmpty()) {
            log.info("根据条件查询菜品，无匹配数据，查询条件：{}", dish);
            return Collections.emptyList();
        }

        // 3. 批量查询分类信息（优化N+1查询）
        List<Long> categoryIds = dishList.stream()
                .map(Dish::getCategoryId)
                .distinct()
                .collect(Collectors.toList());
        Map<Long, String> categoryNameMap = categoryService.listByIds(categoryIds).stream()
                .collect(Collectors.toMap(Category::getId, Category::getName));

        // 4. 批量查询口味信息（优化N+1查询）
        List<Long> dishIds = dishList.stream()
                .map(Dish::getId)
                .collect(Collectors.toList());
        LambdaQueryWrapper<DishFlavor> flavorWrapper = new LambdaQueryWrapper<>();
        flavorWrapper.in(DishFlavor::getDishId, dishIds);
        Map<Long, List<DishFlavor>> dishFlavorMap = dishFlavorService.list(flavorWrapper).stream()
                .collect(Collectors.groupingBy(DishFlavor::getDishId)); // 菜品ID→口味列表映射

        // 5. 转换为DishDto（补充分类名称和口味列表）
        return dishList.stream()
                .map(item -> {
                    DishDto dto = new DishDto();
                    BeanUtils.copyProperties(item, dto);
                    // 补充分类名称
                    dto.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
                    // 补充口味列表（无口味时返回空列表，避免null）
                    dto.setFlavors(dishFlavorMap.getOrDefault(item.getId(), Collections.emptyList()));
                    return dto;
                })
                .collect(Collectors.toList());
    }

    /**
     * 批量删除菜品（含关联口味数据，校验在售状态）
     *
     * @param ids 菜品ID字符串（多个ID用逗号分隔）
     * @return 操作结果（成功/失败信息）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> deleteByIds(String ids) {
        // 1. 入参校验
        if (ids == null || ids.trim().isEmpty()) {
            return R.error("菜品ID列表不能为空");
        }

        // 2. 解析ID列表（去空格、过滤空字符串）
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(String::trim)
                .filter(idStr -> !idStr.isEmpty())
                .map(Long::valueOf)
                .collect(Collectors.toList());
        if (idList.isEmpty()) {
            return R.error("菜品ID列表不能为空");
        }

        // 3. 校验是否存在在售菜品（在售菜品不允许删除）
        long onSaleCount = this.count(new LambdaQueryWrapper<Dish>()
                .in(Dish::getId, idList)
                .eq(Dish::getStatus, 1)); // 1=在售
        if (onSaleCount > 0) {
            log.warn("批量删除菜品失败，存在在售菜品，涉及ID：{}", idList);
            return R.error("存在在售菜品，无法删除");
        }

        // 4. 删除关联的口味数据（dish_flavor表）
        LambdaQueryWrapper<DishFlavor> flavorDeleteWrapper = new LambdaQueryWrapper<>();
        flavorDeleteWrapper.in(DishFlavor::getDishId, idList);
        dishFlavorService.remove(flavorDeleteWrapper);

        // 5. 删除菜品数据（dish表）
        boolean deleteSuccess = this.removeByIds(idList);
        if (deleteSuccess) {
            log.info("批量删除菜品成功，删除数量：{}，涉及ID：{}", idList.size(), idList);
            return R.success("删除成功");
        } else {
            log.error("批量删除菜品失败，涉及ID：{}", idList);
            return R.error("删除失败，请重试");
        }
    }
}